﻿using System;
using System.Collections.Generic;
using System.Linq;
using Shell.Common;
using Shell.KnowledgeBase;

namespace Shell.Nodes
{
    public class ContextMatchNode : BranchNode
    {
        private readonly Transaction _transaction;
        private readonly int _index;
        private readonly BindingContext _bindingContext;
        private readonly List<Statement> _context;
        private readonly Func<Statement, BindingContext, Node> _next;

        public ContextMatchNode(
            Transaction transaction,
            int index,
            BindingContext bindingContext,
            List<Statement> context,
            Func<Statement, BindingContext, Node> next)
        {
            _transaction = transaction;
            _index = index;
            _bindingContext = bindingContext;
            _context = context;
            _next = next;
        }

        public override IEnumerable<WeightedNode> Process()
        {
            var stringsToSearch = _transaction.Messages.Take(_index).ToArray();

            var weight = CountIncludes(_context, stringsToSearch) * 2 - stringsToSearch.Count();

            var subgoal = _next(_transaction.Messages[_index + 1].Resolve(_bindingContext), _bindingContext);

            return Identity(subgoal.Yield());
        }

        public override string ToString()
        {
            return String.Format(
                "Match context: {0}",
                String.Join("\n", _transaction.Messages.Select(_ => _.ToString())));
        }

        public static IEnumerable<Tuple<int, BindingContext>> Match(Transaction transaction, Statement s)
        {
            for (var i = 0; i < transaction.Messages.Count(); i++ )
            {
                var bindingContext = new BindingContext();

                if (UnificationModule.UnifyStatements(transaction.Messages[i], s, bindingContext))
                {
                    yield return Tuple.Create(i, bindingContext);
                }
            }
        }

        public static int CountIncludes(IEnumerable<Statement> context, IEnumerable<Statement> statements)
        {
            var workingContext = context.ToList();
            var result = 0;

            foreach (var statement in statements)
            {
                if (workingContext.Contains(statement, new UnificationModule.UnificationComparer()))
                {
                    result++;
                    workingContext.Remove(statement);
                }
            }

            return result;
        }
    }
}